﻿using System;
using System.Collections.Generic;
using System.Reflection;

namespace DownKyi.Core.Danmaku2Ass
{
    /// <summary>
    /// 显示方式
    /// </summary>
    public class Display
    {
        public Config Config;
        public Danmaku Danmaku;
        public int LineIndex;

        public int FontSize;
        public bool IsScaled;
        public int MaxLength;
        public int Width;
        public int Height;

        public Tuple<int, int> Horizontal;
        public Tuple<int, int> Vertical;

        public int Duration;
        public int Leave;

        protected Display() { }

        public Display(Config config, Danmaku danmaku)
        {
            Config = config;
            Danmaku = danmaku;
            LineIndex = 0;

            IsScaled = SetIsScaled();
            FontSize = SetFontSize();
            MaxLength = SetMaxLength();
            Width = SetWidth();
            Height = SetHeight();

            Horizontal = SetHorizontal();
            Vertical = SetVertical();

            Duration = SetDuration();
            Leave = SetLeave();
        }

        /// <summary>
        /// 根据弹幕样式自动创建对应的 Display 类
        /// </summary>
        /// <returns></returns>
        public static Display Factory(Config config, Danmaku danmaku)
        {
            Dictionary<string, Display> dict = new Dictionary<string, Display>
            {
                { "scroll", new ScrollDisplay(config, danmaku) },
                { "top", new TopDisplay(config, danmaku) },
                { "bottom", new BottomDisplay(config, danmaku) }
            };
            return dict[danmaku.Style];
        }

        /// <summary>
        /// 字体大小
        /// 按用户自定义的字体大小来缩放
        /// </summary>
        /// <returns></returns>
        protected int SetFontSize()
        {
            if (IsScaled)
            {
                Console.WriteLine($"{Danmaku.SizeRatio}");
            }
            return Utils.IntCeiling(Config.BaseFontSize * Danmaku.SizeRatio);
        }

        /// <summary>
        /// 字体是否被缩放过
        /// </summary>
        /// <returns></returns>
        protected bool SetIsScaled()
        {
            return !Math.Round(Danmaku.SizeRatio, 2).Equals(1.0);
            //return Danmaku.SizeRatio.Equals(1.0f);
        }

        /// <summary>
        /// 最长的行字符数
        /// </summary>
        /// <returns></returns>
        protected int SetMaxLength()
        {
            string[] lines = Danmaku.Content.Split('\n');
            int maxLength = 0;
            foreach (string line in lines)
            {
                int length = Utils.DisplayLength(line);
                if (maxLength < length)
                {
                    maxLength = length;
                }
            }
            return maxLength;
        }

        /// <summary>
        /// 整条字幕宽度
        /// </summary>
        /// <returns></returns>
        protected int SetWidth()
        {
            float charCount = MaxLength;// / 2;
            return Utils.IntCeiling(FontSize * charCount);
        }

        /// <summary>
        /// 整条字幕高度
        /// </summary>
        /// <returns></returns>
        protected int SetHeight()
        {
            int lineCount = Danmaku.Content.Split('\n').Length;
            return lineCount * FontSize;
        }

        /// <summary>
        /// 出现和消失的水平坐标位置
        /// 默认在屏幕中间
        /// </summary>
        /// <returns></returns>
        protected virtual Tuple<int, int> SetHorizontal()
        {
            int x = (int)Math.Floor(Config.ScreenWidth / 2.0);
            return Tuple.Create(x, x);
        }

        /// <summary>
        /// 出现和消失的垂直坐标位置
        /// 默认在屏幕中间
        /// </summary>
        /// <returns></returns>
        protected virtual Tuple<int, int> SetVertical()
        {
            int y = (int)Math.Floor(Config.ScreenHeight / 2.0);
            return Tuple.Create(y, y);
        }

        /// <summary>
        /// 整条字幕的显示时间
        /// </summary>
        /// <returns></returns>
        protected virtual int SetDuration()
        {
            int baseDuration = 3 + Config.TuneDuration;
            if (baseDuration <= 0)
            {
                baseDuration = 0;
            }
            float charCount = MaxLength / 2;

            int value;
            if (charCount < 6)
            {
                value = baseDuration + 1;
            }
            else if (charCount < 12)
            {
                value = baseDuration + 2;
            }
            else
            {
                value = baseDuration + 3;
            }
            return value;
        }

        /// <summary>
        /// 离开碰撞时间
        /// </summary>
        /// <returns></returns>
        protected virtual int SetLeave()
        {
            return (int)(Danmaku.Start + Duration);
        }

        /// <summary>
        /// 按照新的行号重新布局
        /// </summary>
        /// <param name="lineIndex"></param>
        public void Relayout(int lineIndex)
        {
            LineIndex = lineIndex;
            Horizontal = SetHorizontal();
            Vertical = SetVertical();
        }
    }


    /// <summary>
    /// 顶部
    /// </summary>
    public class TopDisplay : Display
    {
        public TopDisplay(Config config, Danmaku danmaku) : base(config, danmaku)
        {
            //Console.WriteLine("TopDisplay constructor.");
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        protected override Tuple<int, int> SetVertical()
        {
            // 这里 y 坐标为 0 就是最顶行了
            int y = LineIndex * Config.BaseFontSize;
            return Tuple.Create(y, y);
        }
    }

    /// <summary>
    /// 底部
    /// </summary>
    public class BottomDisplay : Display
    {
        public BottomDisplay(Config config, Danmaku danmaku) : base(config, danmaku)
        {
            //Console.WriteLine("BottomDisplay constructor.");
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        protected override Tuple<int, int> SetVertical()
        {
            // 要让字幕不超出底部，减去高度
            int y = Config.ScreenHeight - (LineIndex * Config.BaseFontSize) - Height;
            // 再减去自定义的底部边距
            y -= Config.BottomMargin;
            return Tuple.Create(y, y);
        }
    }

    /// <summary>
    /// 滚动
    /// </summary>
    public class ScrollDisplay : Display
    {
        public int Distance;
        public int Speed;

        public ScrollDisplay(Config config, Danmaku danmaku) : base()
        {
            //Console.WriteLine("ScrollDisplay constructor.");

            Config = config;
            Danmaku = danmaku;
            LineIndex = 0;

            IsScaled = SetIsScaled();
            FontSize = SetFontSize();
            MaxLength = SetMaxLength();
            Width = SetWidth();
            Height = SetHeight();

            Horizontal = SetHorizontal();
            Vertical = SetVertical();

            Distance = SetDistance();
            Speed = SetSpeed();

            Duration = SetDuration();
            Leave = SetLeave();
        }

        /// <summary>
        /// ASS 的水平位置参考点是整条字幕文本的中点
        /// </summary>
        /// <returns></returns>
        protected override Tuple<int, int> SetHorizontal()
        {
            int x1 = Config.ScreenWidth + (int)Math.Floor(Width / 2.0);
            int x2 = 0 - (int)Math.Floor(Width / 2.0);
            return Tuple.Create(x1, x2);
        }

        protected override Tuple<int, int> SetVertical()
        {
            int baseFontSize = Config.BaseFontSize;

            // 垂直位置，按基准字体大小算每一行的高度
            int y = (LineIndex + 1) * baseFontSize;

            // 个别弹幕可能字体比基准要大，所以最上的一行还要避免挤出顶部屏幕
            // 坐标不能小于字体大小
            if (y < FontSize)
            {
                y = FontSize;
            }
            return Tuple.Create(y, y);
        }

        /// <summary>
        /// 字幕坐标点的移动距离
        /// </summary>
        /// <returns></returns>
        protected int SetDistance()
        {
            Tuple<int, int> x = Horizontal;
            return x.Item1 - x.Item2;
        }

        /// <summary>
        /// 字幕每个字的移动的速度
        /// </summary>
        /// <returns></returns>
        protected int SetSpeed()
        {
            // 基准时间，就是每个字的移动时间
            // 12 秒加上用户自定义的微调
            int baseDuration = 12 + Config.TuneDuration;
            if (baseDuration <= 0)
            {
                baseDuration = 1;
            }
            return Utils.IntCeiling(Config.ScreenWidth / baseDuration);
        }

        /// <summary>
        /// 计算每条弹幕的显示时长，同步方式
        /// 每个弹幕的滚动速度都一样，辨认度好，适合观看剧集类视频。
        /// </summary>
        /// <returns></returns>
        public int SyncDuration()
        {
            return Distance / Speed;
        }

        /// <summary>
        /// 计算每条弹幕的显示时长，异步方式
        /// 每个弹幕的滚动速度都不一样，动态调整，辨认度低，适合观看 MTV 类视频。
        /// </summary>
        /// <returns></returns>
        public int AsyncDuration()
        {
            int baseDuration = 6 + Config.TuneDuration;
            if (baseDuration <= 0)
            {
                baseDuration = 0;
            }
            float charCount = MaxLength / 2;

            int value;
            if (charCount < 6)
            {
                value = (int)(baseDuration + charCount);
            }
            else if (charCount < 12)
            {
                value = baseDuration + (int)(charCount / 2);
            }
            else if (charCount < 24)
            {
                value = baseDuration + (int)(charCount / 3);
            }
            else
            {
                value = baseDuration + 10;
            }
            return value;
        }

        /// <summary>
        /// 整条字幕的移动时间
        /// </summary>
        /// <returns></returns>
        protected override int SetDuration()
        {
            string methodName = Config.LayoutAlgorithm.Substring(0, 1).ToUpper() + Config.LayoutAlgorithm.Substring(1);
            methodName += "Duration";
            MethodInfo method = typeof(ScrollDisplay).GetMethod(methodName);
            if (method != null)
            {
                return (int)method.Invoke(this, null);
            }
            return 0;
        }

        /// <summary>
        /// 离开碰撞时间
        /// </summary>
        /// <returns></returns>
        protected override int SetLeave()
        {
            // 对于滚动样式弹幕来说，就是最后一个字符离开最右边缘的时间
            // 坐标是字幕中点，在屏幕外和内各有半个字幕宽度
            // 也就是跑过一个字幕宽度的路程
            float duration = Width / Speed;
            return (int)(Danmaku.Start + duration);
        }

    }

}
